module oderogue.tcod.Color;

import derelict.tcod.libtcod;

public alias TCOD_colctrl_t ColorControl;

/**
 * Note that the Doryen library uses 32bits colors. Thus, your OS desktop must use 32bits colors.
 * A color is defined by its red, green and blue component between 0 and 255.
 */
public struct Color
{
	/// Needs to be public for now, hopefully will be changed in a later release of DMD.
	private TCOD_color_t color;
	
	/**
	 * Create a new Color with the given values.
	 *
	 * Params:
	 *	red = the red value of the color
	 *	green = the green value of the color
	 *	blue = the blue value of the color
	 */
	public this(ubyte red, ubyte green, ubyte blue)
	{
		color.r = red;
		color.g = green;
		color.b = blue;
	}
	
	private this(TCOD_color_t c)
	{
		color = c;
	}
	
	@property
	{
		public ubyte red() { return color.r; }
		public void red(ubyte red) { color.r = red; }

		public ubyte green() { return color.g; }
		public void green(ubyte green) { color.g = green; }

		public ubyte blue() { return color.b; }
		public void blue(ubyte blue) { color.b = blue; }
			
		public TCOD_color_t data() { return color; }
	}
	
	/**
	 * Compare this color with the given color.
	 *
	 * Params:
	 *	c = the color to compare against
	 * 
	 * Returns: true if both colors are equal, false otherwise.
	 */
	public bool opEquals(ref const Color c) const
	{
		return TCOD_color_equals(color, c.color);
	}
	
	/**
	 * Add, subtract or multiply this color the the given color.
	 *
	 * Params:
	 *	c = the color which is used in the calculation
	 *
	 * Returns: A new color as the result from the calculation.
	 */
	public Color opBinary(string op)(Color c) if (op == "+" || op == "-" || op == "*") 
	{
		static if (op == "+")
		{
			return Color(TCOD_color_add(color, c.data));
		}
		static if(op == "-")
		{
			return Color(TCOD_color_subtract(color, c.data));
		}
		static if(op == "*")
		{
			return Color(TCOD_color_multiply(color, c.data));
		}
	}
	unittest
	{
		auto red = Color(255, 0, 0);
		auto green = Color(0, 255, 0);
		
		auto test1 = red + green;
		assert(test1.red == 255);
		assert(test1.green == 255);
		assert(test1.blue == 0);
	}
	
	/**
	 * Multiply this color by the given float value.
	 *
	 * Params:
	 *	f = the float with which to multiply
	 *
	 * Returns: A new color as the result from the multiplication
	 */
	public Color opBinary(string op)(float f) if (op == "*")
	{
		return Color(TCOD_color_multiply_scalar(color, f));
	}
	
	/**
	 * Interpolate between this color and c. The coef should be between values 0.0 and 1.0, but other values are possible too.
	 * 
	 * Params:
	 *	c = the other color to interpolate to
	 *	coef = the co-efficient in the interpolation
	 *
	 * Returns: A new color as the result from the interpolation.
	 */
	public Color lerp(Color c, float coef)
	{
		return Color(TCOD_color_lerp(color, c.color, coef));
	}
	
	/**
	 * Set the color value using hue, saturation and value components. This will modify the current color.
	 * Legal values are:<br>
	 *	0.0 <= hue < 360.0<br>
	 *	0.0 <= saturation <= 1.0<br>
	 *	0.0 <= value <= 1.0<br>
	 *
	 * Params:
	 *	hue = the hue of the new color
	 *	saturation = the saturation of the new color
	 *	value = the value of the color
	 */
	public void setHSV(float hue, float saturation, float value)
	in
	{
		assert (hue >= 0 && hue <= 360);
		assert (saturation >= 0 && saturation <= 1);
		assert (value >= 0 && value <= 1);
	}
	body
	{
		TCOD_color_set_HSV(&color, hue, saturation, value);
	}
	
	/**
	 * Gets the hue, saturation and value components of this color.
	 * 
	 * Params:
	 *	hue = The parameter that will contain the hue component
	 *	saturation = the parameter that will contain the saturation component
	 *	value = the parameter that will contain the value component
	 */
	public void getHSV(out float hue, out float saturation, out float value)
	{
		TCOD_color_get_HSV(color, &hue, &saturation, &value);
	}
	
	/**
	 * Generate a color map form the given key colors. The map's colors will be interpolated between the keys.
	 * 
	 * Params:
	 *	keyColors = an array of colors containing the color of each key
	 *	keyIndices = an array of colors containing the index of each key
	 *
	 * Returns: The generated color map. It's length is the last key index + 1.
	 */
	public Color[] genMap(Color[] keyColors, int[] keyIndices)
	in
	{
		assert (keyColors.length == keyIndices.length);
	}
	body
	{
		// First convert keyColors to an array to TCOD_color_t's
		auto tcodColors = convertColorArrayToTCODColorArray(keyColors);
		// Create an array for the map
		auto map = new TCOD_color_t[keyIndices[$ - 1] + 1];
		// Generate the map
		TCOD_color_gen_map(map.ptr, keyIndices.length, tcodColors.ptr, keyIndices.ptr);
		// Convert the map array to a Color array
		return convertTCODArrayToColorArray(map);
	}
	
	private Color[] convertTCODArrayToColorArray(TCOD_color_t[] map)
	{
		auto colors = new Color[map.length];
		foreach (i, color ; map)
		{
			colors[i] = Color(color);
		}
		return colors;
	}
	
	private TCOD_color_t[] convertColorArrayToTCODColorArray(Color[] keyColors)
	{
		auto tcodColors = new TCOD_color_t[keyColors.length];
		foreach (i, color ; keyColors)
		{
			tcodColors[i] = color.color;
		}
		return tcodColors;
	}
}